#include "QGCMapEngine.h"
#include "QGeoTiledMappingManagerEngineQGC.h"
#include "QGeoTileFetcherQGC.h"

#include <QtLocation/private/qgeocameracapabilities_p.h>
#include <QtLocation/private/qgeomaptype_p.h>
#include <QtLocation/private/qgeotiledmap_p.h>
#include <QtLocation/private/qgeofiletilecache_p.h>

#include <QDir>
#include <QStandardPaths>

//-----------------------------------------------------------------------------
/**
 * @brief QGeoTiledMapQGC构造函数
 * 
 * 初始化QGC瓦片地图实例
 * 
 * @param engine 地图引擎指针
 * @param parent 父对象指针
 */
QGeoTiledMapQGC::QGeoTiledMapQGC(QGeoTiledMappingManagerEngine *engine, QObject *parent)
    : QGeoTiledMap(engine, parent)
{

}

//-----------------------------------------------------------------------------
/**
 * @brief QGeoTiledMappingManagerEngineQGC构造函数
 * 
 * 初始化QGC瓦片地图管理引擎，设置相机能力、地图类型、缓存等
 * 
 * @param parameters 初始化参数
 * @param error 错误代码指针，用于返回错误状态
 * @param errorString 错误信息字符串指针，用于返回错误描述
 */
QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
:   QGeoTiledMappingManagerEngine()
{
    // 相机类，用来设置地图参数，比如最大最小缩放，方位角调整等.
    QGeoCameraCapabilities cameraCaps;
    cameraCaps.setMinimumZoomLevel(2.0);
    cameraCaps.setMaximumZoomLevel(MAX_MAP_ZOOM);
    cameraCaps.setSupportsBearing(true);
    setCameraCapabilities(cameraCaps);

    // 这个256x256是Google Maps简历的标准，后来被其他地图服务商广泛采用. 256是2^8, 计算机处理起来是最快的。
    setTileSize(QSize(256, 256));

    // 谷歌和必应地图提供商的实现存在问题或者不完整，这个代码是从OpenPilot中修改的。
    QList<QGeoMapType> mapList;
    // 获取地图提供者列表，其中getQGCMapEngine()是一个全局函数, 会返回一个QGCMapEngine实例
    QHash<QString, MapProvider*> providerTable = getQGCMapEngine()->urlFactory()->getProviderTable();
    for (auto it = providerTable.begin(); it != providerTable.end(); ++it) {
        // 使用 it.key() 获取键，it.value() 获取值
        QString key = it.key();
        MapProvider *provider = it.value();
        // 这里添加您的处理逻辑
        if (!provider->_isElevationProvider()) {
            mapList.append(QGeoMapType(provider->getMapStyle(), key, key, false, false, 
                                       getQGCMapEngine()->urlFactory()->getIdFromType(key), 
                                       QByteArray("QGroundControl"), cameraCaps));
        }
    }

    // 调用基类方法，设置支持的地图类型
    setSupportedMapTypes(mapList);

    // QGCMapEngine.cpp的构造函数中定义了代理, 默认是有一些地图不需要代理密钥，但是需要更好的服务或者特定地图提供商是需要密钥的
    //-- Users (QML code) can define a different user agent
    if (parameters.contains(QStringLiteral("useragent"))) {
        getQGCMapEngine()->setUserAgent(parameters.value(QStringLiteral("useragent")).toString().toLatin1());
    }

    _setCache(parameters);

    // 设置瓦片获取器
    // 当地图需要显示特定瓦片的时候，会调用getTileImage()方法
    // QGeoTileFetcherQGC会尝试从本地缓存获取瓦片，如果缓存中没有，则发起网络请求获取瓦片
    setTileFetcher(new QGeoTileFetcherQGC(this));

    *error = QGeoServiceProvider::NoError;
    errorString->clear();
}

//-----------------------------------------------------------------------------
/**
 * @brief QGeoTiledMappingManagerEngineQGC析构函数
 * 
 * 清理QGC瓦片地图管理引擎资源
 */
QGeoTiledMappingManagerEngineQGC::~QGeoTiledMappingManagerEngineQGC()
{
}

//-----------------------------------------------------------------------------
/**
 * @brief 创建地图实例
 * 
 * 创建并返回一个新的QGeoTiledMapQGC实例
 * 
 * @return 新创建的地图实例指针
 */
QGeoMap*
QGeoTiledMappingManagerEngineQGC::createMap()
{
    return new QGeoTiledMapQGC(this);
}

//-----------------------------------------------------------------------------
/**
 * @brief 设置地图缓存
 * 
 * 根据参数设置地图的磁盘缓存路径和内存缓存大小
 * 
 * @param parameters 初始化参数，包含缓存相关设置
 */
void
QGeoTiledMappingManagerEngineQGC::_setCache(const QVariantMap &parameters)
{
    QString cacheDir;
    if (parameters.contains(QStringLiteral("mapping.cache.directory")))
        cacheDir = parameters.value(QStringLiteral("mapping.cache.directory")).toString();
    else {
        cacheDir = getQGCMapEngine()->getCachePath();
        if(!QFileInfo::exists(cacheDir)) {
            if(!QDir::root().mkpath(cacheDir)) {
                qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
                cacheDir = QDir::homePath() + QStringLiteral("/.qgcmapscache/");
            }
        }
    }
    if(!QFileInfo::exists(cacheDir)) {
        if(!QDir::root().mkpath(cacheDir)) {
            qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
            cacheDir.clear();
        }
    }
    //-- Memory Cache
    uint32_t memLimit = 0;
    if (parameters.contains(QStringLiteral("mapping.cache.memory.size"))) {
      bool ok = false;
      memLimit = parameters.value(QStringLiteral("mapping.cache.memory.size")).toString().toUInt(&ok);
      if (!ok)
          memLimit = 0;
    }
    if(!memLimit)
    {
        //-- Value saved in MB
        memLimit = getQGCMapEngine()->getMaxMemCache() * (1024 * 1024);
    }
    //-- It won't work with less than 1M of memory cache
    if(memLimit < 1024 * 1024)
        memLimit = 1024 * 1024;
    //-- On the other hand, Qt uses signed 32-bit integers. Limit to 1G to round it down (you don't need more than that).
    if(memLimit > 1024 * 1024 * 1024)
        memLimit = 1024 * 1024 * 1024;
    //-- Disable Qt's disk cache (sort of)
    QAbstractGeoTileCache *pTileCache = new QGeoFileTileCache(cacheDir);
    setTileCache(pTileCache);
    if(pTileCache)
    {
        //-- We're basically telling it to use 100k of disk for cache. It doesn't like
        //   values smaller than that and I could not find a way to make it NOT cache.
        //   We handle our own disk caching elsewhere.
        pTileCache->setMaxDiskUsage(1024 * 100);
        pTileCache->setMaxMemoryUsage(memLimit);
    }
}